#light

open Langexplr.Abc
open Langexplr.Abc.InstructionUtilities
open Langexplr.Abc.AvmActivePatterns
open System.IO
open System


//let args = System.Environment.GetCommandLineArgs()
//Workaround for compatibility problem with mono
let args = typeof<System.Environment>.GetMethod("GetCommandLineArgs").Invoke(null,[||]) :?> string array
let sourceFile = args.[1]
let targetFileName = args.[1].Substring(0,args.[1].IndexOf('.')) + "_t.abc";;

let modification = 
      if args.Length = 3 then
         2
      else
         1

let abcFile = using (new FileStream(sourceFile,FileMode.Open)) ( 
                    fun stream -> AvmAbcFile.Create(stream))


let printInstructions = 
   [Pop;
    FindPropertyStrict(CQualifiedName(Ns("",NamespaceKind.Namespace),
                                                                   "print"));
    PushString "Hola!";
    CallProperty(CQualifiedName(Ns("",NamespaceKind.Namespace),"print"),1);Pop]


let modifyCode(b:AvmMethodBody option) = 
  match b with
  | Some(AvmMethodBody(lc,ms,isd,msd,instructions,ex,traits)) ->      
       AvmMethodBody(lc,
                     ms,
                     isd,
                     msd,
                     (List.foldBack
                            (fun i r -> 
                                match i with
                                | Pop -> 
                                      (List.append 
                                         printInstructions   r) 
                                | _ -> (i::r)) 
                            (Array.toList instructions) [])
                     |> List.toArray,
                     ex,traits) |> Some

let modifyFile(f:AvmAbcFile) =
  match f with
  | AvmAbcFile(
               [AvmScript(
                          AvmMethod(name,
                                    returnType,
                                    parameters,
                                    body),smembers)],
               classes) -> 
               AvmAbcFile(
                          [AvmScript(
                                     AvmMethod(name,
                                               returnType,
                                               parameters,
                                               modifyCode(body)),smembers)],
                          classes)

let addClassMethod(aClass,newMethod) =
   match aClass with
   | AvmClass(name,
              superclassname,
              init,
              cinit,
              slots,
              methods,
              properties,
              pns)  ->
       AvmClass(name,
                superclassname,
                init,cinit,
                slots,
                newMethod::methods,
                properties,
                pns)

let newFooMethod(message:string) = 
    AvmMemberMethod( 
      CQualifiedName(Ns("",NamespaceKind.PackageNamespace),"foo"),
      AvmMethod(
         "",
         SQualifiedName("*"),
         [||],
         Some <| 
           AvmMethodBody(
             2,1,4,5,
             [|
               GetLocal0;
               PushScope;
               FindPropertyStrict(
                    MQualifiedName(
                       [|Ns("",
                            NamespaceKind.PackageNamespace)|],
                       "print"));
               PushString message;
               CallProperty(
                    MQualifiedName(
                       [|Ns("",
                            NamespaceKind.PackageNamespace)|],
                       "print"),
                    1);
               Pop;
               ReturnVoid
               |],[||],[||]             
           )          
      ),
      AbcTraitAttribute.Override
    )

let modifyFileToAddMethod(f:AvmAbcFile) =
  AvmAbcFile(f.Scripts,
             f.Classes |>
                List.map (fun (c:AvmClass) ->
                           
                           match c.Name  with
                           | CQualifiedName(_,"Rectangle") ->
                               addClassMethod(
                                    c,
                                    newFooMethod("New foo for Rectange!!!"))
                           | _ -> c))

printf "Performing modification %d \n" modification

let modifiedAbcFile = 
      match modification with
      | 2 -> modifyFileToAddMethod(abcFile)
      | _ -> modifyFile(abcFile)


printf "loaded!\n"

let abcFileCreator = AbcFileCreator()
let file = modifiedAbcFile.ToLowerIr(abcFileCreator)

let foo(f) =
   printf "%s" (f.ToString()) 
foo(file)

using (new BinaryWriter(new FileStream(targetFileName,FileMode.Create))) 
      (fun f -> file.WriteTo(f))


let abcFile2 = using (new FileStream(targetFileName,FileMode.Open)) ( 
                    fun stream -> AvmAbcFile.Create(stream))

printf "loaded2!"
